


#include "math.h"
#include "sdlmisc.h"

#include "graphics.h"
#include <iostream>
#include "glext.h"


using namespace WtfGraphics;
Screen *	Screen::MainScreen;


// _TEST_ : OpenGL lights
float LightAmbient[]= { 1.0f, 1.0f, 1.0f, 1.0f};
float LightDiffuse[]= { 0.5f, 0.6f, 0.6f, 1.0f};
float LightPosition[]= { 50, 100.0f, -50, 1.0f};
float LightSpecular[]= {1.0f, 1.0f, 1.0f , 1.0f};



// CANVAS CLASS
// Constructor
Canvas::Canvas()
: WtfEngine::DataObject("Canvas") {
	this->surface = NULL;
}

Canvas::Canvas(iVector2D dim)
: WtfEngine::DataObject("Canvas") {
	this->surface = NULL;
}

Canvas::Canvas(SDL_Surface *s)
: WtfEngine::DataObject("Canvas") {
	this->surface = s;
}

// Destructor
Canvas::~Canvas() {
	this->Unload();
}

// get dimensions of canvas
iVector2D Canvas::dim() { return iVector2D(this->surface->w, this->surface->h); }

SDL_Surface *Canvas::getSurface() {return this->surface;}


// Sets the destination rect such that the image would be drawn centered to the *surface
/*void Canvas::CenterDestRect(SDL_Surface *surface) {
    this->drect.x = (surface->w - this->surface->w)/2;
    this->drect.y = (surface->h - this->surface->h)/2;
}

void Canvas::SetDestPosition(int x, int y) {
	this->drect.x = x;
	this->drect.y = y;
}

// draws on the selected surface
void Canvas::DrawOn(SDL_Surface *surface) {
    SDL_BlitSurface(this->surface,&this->srect,surface,&this->drect);
}

// draws on the selected surface at given position
void Canvas::DrawOnAt(SDL_Surface *surface, int x, int y) {
	SetDestPosition(x, y);
	DrawOn(surface);
}*/

// copy another canvas onto this one
/*void Canvas::putCanvas(Canvas *c, iVector2D pos)
{
};

// plot a pixel of a certain colour to the surface
void Canvas::putPixel(iVector2D pos, Colour c)
{
	
};

// draw a circle of radius r
void Canvas::putCircle(iVector2D pos, tScalar r, Colour c)
{
	
};

// draw a line segment
void Canvas::putLine(iLineSegment *l, Colour c)
{
};

// draw a straight line segment from (x1, y1) to (x2, y2), smoothly changing between color1 and color2
void Canvas::putGradientLine(iLineSegment l, Colour c1, Colour c2)
{
	
};
*/

// loads and image and automatically sets the source rect to cover the whole picture.
// By default, the picture is aligned to 0,0 on the destination surface
void Image::LoadBMP(char *path) {
	// Unload existing image before trying to load another
	this->Unload();
	this->surface = SDL_LoadBMP(path);

    /*this->srect.x=0;
    this->srect.y=0;
    this->srect.w=Surface->w;
	this->srect.h=Surface->h;
	this->drect = this->srect;*/
};

// Unloads the image, if one is loaded
void Canvas::Unload() {
	if(this->surface)
		SDL_FreeSurface(this->surface);
};

// initialize OpenGL
void Screen::InitGL()
{
	// Shading
	glShadeModel(GL_SMOOTH);
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

	glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
	glClearDepth(1.0f);
	
	// Features
	glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);

	glEnable (GL_POLYGON_SMOOTH);


	clearScreen();
	SDL_GL_SwapBuffers();
	
	glViewport(0,0, this->dim().x(), this->dim().y());
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	

	// initial camera setup
//	glOrtho(0.0f, this->dim().x(), this->dim().y(), 0.0f, -1.0f, 1.0f);
	gluPerspective(45.0f,(tScalar)(this->dim().x())/(tScalar)(this->dim().y()),0.1f,2000.0f);
	//gluLookAt(0,500.0f,-800.0f,0.0f,0.0f,0.0f,0.0f,1.0f,0.0f);


	// Light settings
	glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmbient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDiffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, LightSpecular);
	glLightfv(GL_LIGHT0, GL_POSITION, LightPosition);
	glEnable(GL_LIGHT0);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
};

void Screen::SetPerspective(){
	gluPerspective(45.0f,(tScalar)(this->dim().x())/(tScalar)(this->dim().y()),0.1f,2000.0f);
};

void Screen::clearScreen()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
};

/*void Screen::putPoint(tVector pos, Colour c)
{
	glColor3f(c.r, c.g, c.b);
	glBegin(GL_POINTS);
	glVertex3f(pos.x(), pos.y(), pos.z());
	glEnd();
};

void Screen::putLine(tVector p1, tVector p2, Colour c)
{
	glColor3f(c.r, c.g, c.b);
	glBegin(GL_LINES);
	glVertex3f(p1.x(), p1.y(), p1.z());
	glVertex3f(p2.x(), p2.y(), p2.z());
	glEnd();
};

void Screen::putGradientLine(tVector p1, tVector p2, Colour c1, Colour c2)
{
	glBegin(GL_LINES);
	glColor3f(c1.r, c1.g, c1.b);
	glVertex3f(p1.x(), p1.y(), p1.z());
	glColor3f(c2.r, c2.g, c2.b);
	glVertex3f(p2.x(), p2.y(), p2.z());
	glEnd();
};
*/
/*
void Screen::putMesh(Mesh& obj, tVector pos, Orientation3D ori, Colour c)
{
	glPushMatrix();
	

	glTranslatef(pos.x(), pos.y(), pos.z());
	glRotatef(ori.x().inDegs(), 1.0f, 0.0f, 0.0f);
	glRotatef(ori.y().inDegs(), 0.0f, 1.0f, 0.0f);
	glRotatef(ori.z().inDegs(), 0.0f, 0.0f, 1.0f);
	
	
	List<Triangle> *ptr;

	ptr = obj->getTriangles();
	Triangle tri;
	
	ListItem<Triangle> *item;
	item = ptr->getFirstItem();
	
	Vertex3D v;
	int i;

	// Draw all the triangles in the list
	glBegin(GL_TRIANGLES);
	while (NULL != item)
	{
		tri = item->value();
		
		glColor3f(c.r,c.g,c.b); // temp - set color light grey 
		
		for(i = 0; i < 3; i++)
		{
			v = tri.getVertices()[i];
			glVertex3f((tScalar)v.x(), (tScalar)v.y(), (tScalar)v.z());
		}

		item = item->nextItem();
	}
	glEnd();

	glPopMatrix();
};
*/

/*
// draw an object to the screen at the correct position
void putObject(GameObject& obj, Colour c)
{
	putMesh(obj.mesh(), obj.position(), obj.orientation());
}
*/





void Screen::SetMode(unsigned w, unsigned h, unsigned col, unsigned opts)
{
	this->surface = SDL_SetVideoMode(w, h, col, opts);
	if (this->surface == NULL)
		throw "Failed to create SDL screen";
};

void Screen::UpdateScreen()
{
	SDL_GL_SwapBuffers();
};


// Initialize all graphics systems to the requested mode
void WtfGraphics::initGraphics(tGraphicsModeRef& mode, tScreenRef& screen)
{
	// create main screen object
	screen = new Screen();

	// enable opengl - this is required
	int videoFlags = SDL_OPENGL;
	
	// enable depth testing
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);

	// enable hardware palette if requested
	if (mode->HWPalette) videoFlags |= SDL_HWPALETTE; 

	// enable gl double buffering if requested
	if (mode->DoubleBuffer)
	{
		SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
		videoFlags |= SDL_GL_DOUBLEBUFFER;
	}

	// use hardware surfaces if requested
	if (mode->HWSurfaces)
	{
		videoFlags |= SDL_HWSURFACE;
	}
	else // otherwise use software surfaces
	{
		videoFlags |= SDL_SWSURFACE;
	}

	// enable fullscreen if requested
	if (mode->Fullscreen) videoFlags |= SDL_FULLSCREEN;
	
	// enable window to be resized, if set
	if (mode->Resizable) videoFlags |= SDL_RESIZABLE;

	int w = mode->resolution.x();
	int h = mode->resolution.y();
	int bpp = mode->bpp;
	
	// Check mode with SDL
	int bppCheck = SDL_VideoModeOK(w, h, bpp, videoFlags);
	if(!bppCheck)
		throw "The requested video mode is not available at any bit depth.";

	if(bppCheck != bpp)
		WtfEngine::Kernel::DebugPrint("Could not use requested bit depth, but attempting to continue.");	

	bpp = bppCheck;

	// set SDL mode, create a window
	screen->SetMode(w, h, bpp, videoFlags);	

	// initialize OpenGL
	screen->InitGL();
};

//default constructor
GraphicsMode::GraphicsMode()
{
	this->resolution = iVector2D();
	this->bpp = 0;
	this->HWSurfaces = false;
	this->DoubleBuffer = false;
	this->HWBlit = false;
	this->HWPalette = false;
	this->Fullscreen = false;
	this->Resizable = false;
};

// destructor
GraphicsMode::~GraphicsMode()
{

}

// get a default graphics mode
void WtfGraphics::defaultGraphicsMode(tGraphicsModeRef& g)
{
	g = new GraphicsMode();
	g->resolution = iVector2D(800, 600);
	g->bpp = 32;
	// XXX - changed this to try and fix flicker with compiz
	g->HWSurfaces = true;
	g->DoubleBuffer = false;
	g->HWBlit = false;
	g->HWPalette = true;
	g->Fullscreen = false;
	g->Resizable = false;
};
